package apiserver

import (
	"git.hortorgames.com/orange/tang/env"
	"git.hortorgames.com/orange/tang/http"
	"git.hortorgames.com/orange/tang/log"
	"git.hortorgames.com/orange/tang/reflectex"
	"git.hortorgames.com/orange/tang/utils"
	"github.com/gin-gonic/gin"
	"reflect"
	"strings"
	"time"
)

func (gs *Server) RegisterInternalController(handler http.IHttpController) {
	midCtx := func(c *gin.Context) {
		ctx := &InternalContext{
			Context:     &http.Context{Context: c},
			RequestTime: time.Now(),
		}
		ctx.SetLogger(log.Global().WithCallerOffset(-1).With("api", ctx.Request().URL.Path))
		c.Set("_ctx", ctx)
		c.Next()
	}

	h := handler
	t := reflect.TypeOf(h)
	v := reflect.ValueOf(h)
	ti := reflectex.TypeInfoOf(t)
	for _, _mi := range ti.Methods {
		mi := _mi
		if !mi.Public || mi.Method.Type.NumIn() < 2 || mi.Method.Type.NumIn() > 3 || mi.Method.Type.In(1) != reflect.TypeOf((*InternalContext)(nil)) {
			continue
		}
		tag, ok := mi.GetTagByTypeInfo(http.TypeInfoOfHttpMethodTag).(*http.HttpMethodTag)
		if !ok || len(tag.Methods) == 0 || tag.Route == "" {
			continue
		}

		handle := func(c *gin.Context) {
			ctx := c.MustGet("_ctx").(*InternalContext)
			c.Set("_ctx", nil)
			utils.Try(func() {
				ps := []reflect.Value{reflect.ValueOf(ctx)}
				var req interface{}
				if mi.Method.Type.NumIn() == 3 {
					pt := mi.Method.Type.In(2)
					p := reflect.New(pt)
					err := decodeInternal(ctx, p.Interface())
					if err != nil {
						ctx.Abort()
						return
					}
					ps = append(ps, p.Elem())
					req = p.Elem().Interface()
				}
				ctx.Logger().Info("[ReqInternal]", "req", utils.JsonEncodeToStringMust(req))
				v.MethodByName(mi.Name).Call(ps)
				ctx.Done()
			}, func(e interface{}) {
				ctx.Logger().Error("http error", "err", e)
				ctx.Abort()
			})
		}

		hs := make([]gin.HandlerFunc, 0, 2)
		hs = append(hs, midCtx, handle)
		for _, m := range tag.Methods {
			if env.IsRelease() && m != "POST" {
				continue
			}
			gs.Router.Handle(m, tag.Route, hs...)
		}
	}
}

func decodeInternal(ctx *InternalContext, params interface{}) (err error) {
	defer func() {
		if err != nil {
			log.Error("decode params error", "err", err.Error())
		}
	}()

	if ctx.Request().Method == "GET" || ctx.ContentType() == "application/x-www-form-urlencoded" {
		err = ctx.Request().ParseForm()
		if err != nil {
			return
		}
		tmp := map[string]interface{}{}
		for k, v := range ctx.Request().Form {
			if len(v) > 0 {
				vv := v[0]
				if strings.HasPrefix(vv, "{") || strings.HasPrefix(vv, "[") {
					var obj interface{}
					err := utils.JsonDecodeString(vv, &obj)
					if err == nil {
						tmp[k] = obj
						continue
					}
				}
				if strings.HasPrefix(vv, "~") {
					vv = vv[1:]
				}
				tmp[k] = vv
			}
		}
		json := utils.JsonEncodeToStringMust(tmp)
		_ = utils.JsonDecodeString(json, params)
	} else if ctx.ContentType() == "application/json" {
		var bytes []byte
		bytes, err = ctx.GetRawData()
		if err != nil {
			return
		}
		_ = utils.JsonDecode(bytes, params)
	} else if ctx.ContentType() == "application/octet-stream" {
		var bytes []byte
		bytes, err = ctx.GetRawData()
		if err != nil {
			return
		}
		err = utils.BonDecode(bytes, params)
		if err != nil {
			return
		}
	}
	return
}
